package core;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import util.IO;

public class QAP {

	public static double bestCost = Double.MAX_VALUE;
	public static Solution bestSolution = null;
	
	public static void DoQAP(String path, Solution solution,
			int numberIterations) {
		
		//Organizar Solução inicial
		solution.setDestinationList(MatrixToVector(solution.getDestination()));
		solution.setFluxList(MatrixToVector(solution.getFlux()));
		
		for (int i = 0; i < numberIterations; i++) {
			// Construir
			Solution newSolution = DoGreedyConstruction(solution, IO.matrixSize, numberIterations);
			// Busca Local
			newSolution = DoLocalSearch(newSolution, numberIterations);
			//verificar se é melhor
			if(newSolution.getCost() < bestCost){
				bestSolution = newSolution;
			}
			// Gravar
			IO.WriteMatrix(path, newSolution, newSolution.getCost());
			
			System.out.println("A");
		}
		IO.WriteMatrixResults(path, bestSolution);

	}
	
	public static Solution DoLocalSearch(Solution solution, int N){
		//verificar vizinhança
		ArrayList<Solution> t = new ArrayList<Solution>();
		for (int i = 0; i < (N*N-N); i++) {
			System.out.println("B");
			Solution temp = new Solution();
			temp.setDestination(solution.getDestination());
			temp.setFlux(solution.getFlux());
			temp.setDestinationList(solution.getDestinationList());
			temp.setFluxList(Do2Troca(solution.getFluxList(), solution.getSize()));
			temp.setCost(EvaluateSolution(temp));
			
			t.add(temp);
		}
		Solution localBest = null;
		//verificar melhor solucao
		for (Solution sol : t) {
			if(localBest == null){
				localBest = sol;
			}else if(sol.getCost() < localBest.getCost()){
				localBest = sol;
			}
		}
		
		return localBest;
	}

	public static double EvaluateSolution(Solution arrayMatrix) {
		int tam = arrayMatrix.getFlux().length;

		double temp = 0;
		for (int i = 0; i < tam; i++) {
			for (int j = 0; j < tam; j++) {
				if (j > i) {
					// resultado += fluxo (ij) * distancia (pi[i] pi[j])
					// quando j > i
					temp += arrayMatrix.getFlux()[i][j]
							* arrayMatrix.getDestination()[i][j];
				}
			}
		}

		return temp;

	}

	public static Solution DoGreedyConstruction(Solution solution, int nMatrix, 
			int N) {
		Solution newSolution = null;
		//Montar rcl
		ArrayList<Solution> rcl = new ArrayList<Solution>();
		
		for (int i = 0; i < N; i++) {
			newSolution = new Solution();
			newSolution.setDestination(solution.getDestination());
			newSolution.setFlux(solution.getFlux());
			newSolution.setDestinationList(solution.getDestinationList());
			newSolution.setFluxList(Do2Troca(solution.getFluxList(), solution.getSize()));
			newSolution.setCost(EvaluateSolution(newSolution));
			
			rcl.add(newSolution);
			
			System.out.println("C");
		}
		
		//escolher randomicamente
		Random r = new Random();
		int num = r.nextInt(rcl.size()-1);
		
		Solution sol = rcl.get(num);
	
		return sol;
	}

	public static ArrayList<Integer> Do2Troca(List<Integer> list, int N) {
		int[] result = new int[N];
		int length = list.size();
		
		ArrayList<Integer> ret = new ArrayList<Integer>(list.size());
		
		ret.addAll(list);

		Random gen = new Random();

		for (int i = 0; i < N; i++) {
			int r = gen.nextInt(length);

			result[i] = ret.get(r);

			ret.set(r, ret.get(length - 1));
			length--;
		}
		
		return ret;
	}

	/*
	 * M�todos auxiliares
	 */

	public static ArrayList<Integer> MatrixToVector(Integer[][] matrix) {
		int tam = matrix.length;
		ArrayList<Integer> vector = new ArrayList<Integer>();
		for (int i = 0; i < tam; i++) {
			for (int j = 0; j < tam; j++) {
				if (j > i) {
					// resultado += fluxo (ij) * distancia (pi[i] pi[j])
					// quando j > i
					vector.add(matrix[i][j]);
				}
			}
		}

		return vector;
	}

	public static Integer[][] VectorToMatrix(ArrayList<Integer> vector, int size) {
		// int tam = vector.size();
		int tam = size;
		Integer[][] matrix = new Integer[tam][tam];

		for (int i = 0; i < tam; i++) {
			for (int j = 0; j < tam; j++) {
				if (j > i) {
					// resultado += fluxo (ij) * distancia (pi[i] pi[j])
					// quando j > i
					matrix[i][j] = vector.remove(0);
				}

			}
		}

		for (int i = 0; i < tam; i++) {
			for (int j = 0; j < tam; j++) {
				if (j == i) {
					matrix[i][j] = 0;
				}
				if (i < j) {
					matrix[j][i] = matrix[i][j];
				}
			}
		}

		return matrix;
	}

	// Escrever matriz na tela
	public static void PrintMatrix(Solution content) {
		int n = content.getFlux().length;
		System.out.println("Tamanho: " + n);
		// printar matrizes
		for (int i = 0; i < n; i++) {
			System.out.println();
			for (int j = 0; j < n; j++) {
				System.out.print(content.getFlux()[i][j] + "\t");
			}
		}

		System.out.println();

		for (int i = 0; i < n; i++) {
			System.out.println();
			for (int j = 0; j < n; j++) {
				System.out.print(content.getDestination()[i][j] + "\t");
			}
		}
	}

}
